// create的所有逻辑

// 具体看文档    www.developer.github.com/v3/repos/

// 创建项目
// 拉取你自己所有的项目列出来， 让用户选安装哪个项目， projectName
// 选完后·在显示所有的版本号 1.0
// 可能还需要用户配置一些数据 来结合渲染项目

const axios = require("axios")
// 加载效果
const ora = require("ora")
// 选择模板
const inquirer = require("inquirer")
// 保存模板的位置
const {downloadDirectory} = require('./constants.js')

// 下载git仓库的库
let downloadGitRepo = require('download-git-repo')
// 异步api强转promise
const { promisify } = require("util")
downloadGitRepo = promisify(downloadGitRepo)

const path = require('path')

let ncp = require("ncp")
ncp = promisify(ncp)


const Metalsmith = require("metalsmith")
// 统一了所有的模板引擎
let {render} = require('consolidate').ejs
render = promisify(render)

const fs = require('fs')

// 可以通过组织加/组织名/repos      拿到所有仓库
// htpps://api.github.com/orgs/zhu-cli/repos   获取组织下的仓库
// 获取项目列表
const fetchRepoList = async () => {
    const { data } = await axios.get("https://api.github.com/orgs/zhu-cli/repos");
    return data;
}

// 可以通过组织加/组织名/repos      拿到所有仓库
// htpps://api.github.com/orgs/zhu-cli/repos   获取组织下的仓库
// 获取tag列表
const fetchTagList = async (repo) => {
    const { data } = await axios.get(`https://api.github.com/repos/zhu-cli/${repo}/tags`);
    return data;
}


// 封装loading效果， 高阶函数 第一个参数是在函数里调用的函数， 第二个参数是传给里面函数的参数
const waitFnloading = (fn, message) => async (...args) => {
    const spinner = ora(message);
    spinner.start();
    const result = await fn(...args);
    spinner.succeed();
    return result
}

const download = async (repo, tag) => {
    // 拼接下载的目录
    let api =  `zhu-cli/${repo}`;

    if(tag) {
        api = api + `#${tag}`;
    }
    // /user/HDR/.template/repo
    
    const dest = `${downloadDirectory}/${repo}`;
    await downloadGitRepo(api, dest)
    return dest

}


module.exports = async (projectName) => {
    // 1）获取所有项目的模板

    // 在获取之前  显示loading   关闭loading
    let repos = await waitFnloading(fetchRepoList, 'fetching template ....')();

    // 只获取仓库的名字
    repos = repos.map((item) => item.name);
    // console.log(repos)

    // 选择的模板 inquirer   类似npm init后
    const {repo} = await inquirer.prompt({
        name: 'repo',
        type: 'list',  // 让用户选择时的类型
        message: 'please choise a template to create project',
        choices: repos  // 供选择的内容
    });
    // repo为用户选择的模板名
    // console.log(repo);


    // 2)通过当前选择的项目，拉取对应的版本

    let tags = await waitFnloading(fetchTagList, "fetch tags ...")(repo);
    tags = tags.map((item) => item.name);

    const {tag} = await inquirer.prompt({
        name: 'tag',
        type: 'list',  // 让用户选择时的类型
        message: 'please choise a tag to create project',
        choices: tags
    });
    // tag为用户选择的版本号
    // console.log(tag)


    // 把模板放在一个临时目录，存好，以备使用
    // 3) 下载仓库 download-git-repo
    const result = await waitFnloading(download, 'download template')(repo, tag);
    console.log(result)  //  下载的目录


    // 拿到了下载文件的目录，直接拷贝当前执行的目录即可
    // 复杂的需要模板渲染，渲染后再拷贝
    // 把template下的文件拷贝到执行命令的目录下
    
    // 4)拷贝操作
    // 这个目录项目名称是否已经存在，存在则提示
    // 如果有ask.js文件，则是复杂的   // .template/xxx
    if(!fs.existsSync(path.join(result, 'ask.js'))){
        await ncp(result, path.resolve(projectName));
    }else{
        // 复杂的模板
        // 把git上目录下载下来，有ask文件则是复杂模板，要用户选择，选择后再编译模板

        // metalsmith 只要是模板编译都需要这个
        // consolidate 整合所有模板

        //让用户填信息
        await new Promise((resolve, reject) => {
            Metalsmith(__dirname)  // 如果传入路径，默认遍历当前路径下的src文件夹
            .source(result)
            .destination(path.resolve(projectName))  // 目的地目录， 渲染好的文件结果回到这里
            .use(async (files, metal, done) => {
                // console.log(files)
                // 拿用户填的ask.js文件
                const args = require(path.join(result, 'ask.js'));
                let obj = await inquirer.prompt(args);
                // console.log(obj);  //用户填写的结果
                const meta = metal.metadata()  // 下一个use里的数据一样
                Object.assign(meta, obj);
                delete files('ask.js');
                done();
            })
            .use((files, metal, done) => {
                console.log(metal.metadata())  // 拿到上面传过来的结果

                const obj = metal.metadata()
                Reflect.ownKeys(files).forEach(async (file) => {
                    // 这个是要处理的 <%
                    if(file.includes('js') || file.includes('json')){
                        let content = files[file].contents.toString();  // 文件的内容
                        if(content.includes('<%')) {
                            content = await render(content, obj)
                            files[file].contents = Buffer.form(content)  // 渲染
                        }
                    }
                })
                done();
            })
            .build((err) => {
                if(err){
                    reject(err)
                }else{
                    resolve();
                }
            })
        })
    }
    


    






    

}